//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#if Gdmp || Gnuf
using Glodon.Gdmp.DB;
using Glodon.Gdmp.UI;
using Glodon.Gdmp.Exceptions;

using Application = Glodon.Gdmp.DB.Application;

#elif Gap
using Glodon.Gap.DB;
using Glodon.Gap.UI;
using Glodon.Gap.Exceptions;
using Application = Glodon.Gap.DB.Application;
#endif
using Glodon.Lookup.Core;
using Glodon.Lookup.Lookup.Core;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Contracts;
using Glodon.Lookup.Services.Enums;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Pages.Tracebale;
using Glodon.Lookup.Core.Contracts;
using Glodon.Lookup.Core.Utils;
using Glodon.Lookup.Core.ModelBase;

using Wpf.Ui.Common;
using Wpf.Ui.Contracts;
using Wpf.Ui.Controls;

using System.Runtime.InteropServices;

using OperationCanceledException = System.OperationCanceledException;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using System.Windows;

using Glodon.Lookup.ViewModels.Objects;

namespace Glodon.Lookup.ViewModels.Pages
{
    public sealed partial class SnoopViewModel : SnoopViewModelBase, ITabGroupViewModel<SnoopHistoryViewModel>, IApiEventHandler
    {
        private readonly ISnackbarService _snackbarService;

        public SearchBoxMediator SearchBoxMediator { get; set; }
        public IList<SnoopHistoryViewModel> TabData { get; set; }

        [ObservableProperty]
        private Visibility _isClearCommandShow;

        [ObservableProperty]
        private SnoopHistoryViewModel _currentViewModel;

        partial void OnCurrentViewModelChanged(SnoopHistoryViewModel value)
        {
            OnSearchTextChanged(SearchBoxMediator.SearchText);
        }

        [RelayCommand]
        private void ClearSearchText()
        {
            SearchBoxMediator.SearchText = string.Empty;
        }
        public SnoopViewModel(ISnackbarService snackbarService)
            : base(snackbarService)
        {
            SearchBoxMediator = new SearchBoxMediator();
            SearchBoxMediator.SearchTextChangedAction += OnSearchTextChanged;

            _snackbarService = snackbarService;
            TabData = new List<SnoopHistoryViewModel>
            {
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.Document },
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.ModelView },
                new SnoopHistoryViewModel(SearchBoxMediator) { Tab = DataTabType.Selection }
            };

            CurrentViewModel = TabData.First();
            IsClearCommandShow = Visibility.Hidden;
        }


        /// <summary>
        /// 判断图元是否存在。
        /// </summary>
        private bool ElementNotExisted(Element element)
        {
            try
            {
                var latestEle = GlodonAPI.Document.GetElement(element.Id);
                if (latestEle == null)
                {
                    return true;
                }
                return false;
            }
            catch (Exception e)
            {
                return true;
            }
        }

        public override void Snoop(SnoopableObject snoopableObject)
        {
            SupressTreeSelectedChangedCommand = true;
            SearchBoxMediator.SearchText = string.Empty;
            if (snoopableObject.Descriptor is IDescriptorEnumerator descriptor && descriptor.IsEmpty == false)
                try
                {
                    IReadOnlyList<SnoopableObject> snoopableObjects = descriptor.ParseEnumerable(snoopableObject);
                    CurrentViewModel.AddHistory(snoopableObjects);
                }
                catch (Exception exception)
                {
                    SnowException("Invalid object", exception.Message);
                }
            else
            {
                CurrentViewModel.AddHistory(new[] { snoopableObject });
            }
            SupressTreeSelectedChangedCommand = false;
        }

        public override async Task Snoop(DataTabType tab)
        {
            try
            {
                IReadOnlyCollection<SnoopableObject> snoopableObjects = Selector.Snoop(tab);
                SnoopHistoryViewModel snoopHistoryViewModel = GetVmByTab(tab);
                if (tab == DataTabType.Selection)
                {
                    snoopHistoryViewModel.ClearHistory();
                }
                snoopHistoryViewModel.AddHistory(snoopableObjects);

                //刷新详情页。
                _ = FetchMembersCommand.ExecuteAsync(null);

            }
            catch (OperationCanceledException exception)
            {
                _snackbarService.Show("操作取消", exception.Message, SymbolRegular.Warning24, ControlAppearance.Caution);
            }
            catch (Exception exception)
            {
                _ = _snackbarService.ShowAsync("数据查找出错", exception.Message, SymbolRegular.ErrorCircle24, ControlAppearance.Danger);
            }
        }
        public override async Task CollectMembers(bool useCached)
        {
            if (CurrentViewModel.SnoopableObjects != null && !CurrentViewModel.SnoopableObjects.Any(sn => sn.IsSelected))
            {
                CurrentViewModel.SnoopableData = Array.Empty<Descriptor>();
                return;
            }

            try
            {
                if (CurrentViewModel is { SnoopHistory: { Count: > 0 } })
                {
                    SnoopableObject selectedSnObj = CurrentViewModel.SnoopHistory.Last().SelectedObject;
                    CurrentViewModel.SnoopableData = useCached ? await selectedSnObj.GetCachedMembersAsync() : await selectedSnObj.GetMembersAsync();
                }
            }
            catch (InvalidObjectException exception)
            {
                SnowException("无效对象", exception.Message);
            }
            catch (InternalException)
            {
                const string message = "API报告的问题，通常由于目标API对象已无效或者内存被释放";
                SnowException("API内部错误", message);
            }
            catch (SEHException)
            {
                const string message = "API报告的问题，通常由于目标API对象已无效或者内存被释放";
                SnowException("API内部错误", message);
            }
            catch (Exception exception)
            {
                SnowException("数据查找出错", exception.Message);
            }
        }

        protected override void OnTreeSelectedChanged(SnoopableObject newObj)
        {
            if (CurrentViewModel.SnoopHistory is { Count: > 0 })
            {
                CurrentViewModel.SnoopHistory.Last().CachedSelectedObject = newObj;
            }

            if (CurrentViewModel is { SnoopHistory: { Count: > 0 } })
            {
                CurrentViewModel.SnoopHistory.Last().RaiseSelectedObjectChanged();
            }
        }

        public void ClearData(DataTabType dataTab = DataTabType.None)
        {
            SearchBoxMediator.SearchText = string.Empty;
            if (dataTab == DataTabType.None)
            {
                CurrentViewModel = TabData.First(vm => vm.Tab == DataTabType.Document);
                foreach (var vm in TabData)
                {
                    vm.ResetHistoryCommand.Execute(0);
                }
            }
            else
            {

                GetVmByTab(dataTab).ResetHistoryCommand.Execute(0);
            }
        }

        public SnoopHistoryViewModel GetVmByTab(DataTabType dataTab)
        {
            return TabData.FirstOrDefault(vm => vm.Tab == dataTab);
        }

        private void OnApplicationIdling_RefreshData(object sender, ApplicationIdlingEventArgs args)
        {
            foreach (var vm in TabData)
            {
                vm.ResetHistoryCommand.Execute(0);
                _ = Snoop(vm.Tab);
            }
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshData;
        }

        private void OnApplicationIdling_ClearData(object sender, ApplicationIdlingEventArgs args)
        {
            if (Application.Get().ActiveDocument != null)
            {
                foreach (var vm in TabData)
                {
                    vm.ResetHistoryCommand.Execute(0);
                    _ = Snoop(vm.Tab);
                }
            }
            else
            {
                ExitCommand.Execute(null);
            }
            UIApplication.Get().Idling -= OnApplicationIdling_ClearData;
        }

        // 文档修改时用来刷新ModelView首页数据（DocumentChanged 里数据不完整）
        private void OnApplicationIdling_RefreshModelView(object sender, ApplicationIdlingEventArgs args)
        {
            GetVmByTab(DataTabType.ModelView).UpdateHome();
            //使用一次后取消注册
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshModelView;
        }

        // 文档选择改变时用来刷新首页数据（SelectionChanged 里数据不完整）
        private void OnApplicationIdling_RefreshHome(object sender, ApplicationIdlingEventArgs args)
        {
            GetVmByTab(DataTabType.Document).UpdateHome();
            GetVmByTab(DataTabType.ModelView).UpdateHome();
            //使用一次后取消注册
            UIApplication.Get().Idling -= OnApplicationIdling_RefreshHome;
        }

        private void UpdateSnoopableObjects(IList<ElementId> added, IList<ElementId> deleted, IList<ElementId> modified)
        {
            try
            {
                //更新文档Tab首页的数据
                if (GetVmByTab(DataTabType.Document).SnoopHistory.Any())
                {
                    SnoopAction homeData = GetVmByTab(DataTabType.Document).SnoopHistory.First();
                    var docHomeObjects = homeData.Objects.ToList();
                    var docHomeInvalidObjs = docHomeObjects.Where(s => s.Object is Element e && !e.IsValidObject());
                    foreach (var item in added)
                    {
                        var element = GlodonAPI.Document.GetElement(item);
                        docHomeObjects.Add(new SnoopableObject(GlodonAPI.Document, element));
                    }
                    GetVmByTab(DataTabType.Document).SnoopHistory.First().Objects = docHomeObjects;
                }

                if (CurrentViewModel.Tab == DataTabType.Document && GetVmByTab(DataTabType.Document).SnoopHistory.Count == 1)
                {
                    //刷新文档视图
                    CurrentViewModel.RaiseSnoopableObjectsChanged();
                }
                //空闲时再刷新 ModelView 数据
                UIApplication.Get().Idling += OnApplicationIdling_RefreshModelView;

            }
            catch (Exception e)
            {
                _snackbarService.ShowAsync(e.Message + e.StackTrace, "更新数据时出错");
                return;
            }
        }
        public void OnUIViewActivated(object sender, UIViewActivatedEventArgs e)
        {
            try
            {
                var modelViewVm = GetVmByTab(DataTabType.ModelView);
                if (modelViewVm != null)
                {
                    var currentModelView = GlodonAPI.Document!.GetElement(e.CurrentActiveView.ModelViewId) as ModelView;
                    if (currentModelView != null)
                    {
                        bool needReset = false;
                        SnoopableObject selectedObject = modelViewVm.SnoopHistory.First().SelectedObject;
                        if (selectedObject != null)
                        {
                            bool selectedIsModelView = selectedObject.Object is ModelView;
                            bool selectedNotExsited = selectedObject.Object is Element ele && !GlodonAPI.ActiveView!.GetElementVisibility(ele.Id);

                            //如果有深入查看的历史，并且查看对象为ModelView或者在新视图中不可见的对象，需要重置历史
                            //（因为所查看的ModelView对象与当前视图不再匹配）
                            needReset = modelViewVm.SnoopHistory.Count > 1 && (selectedIsModelView || selectedNotExsited);
                        }

                        if (needReset)
                        {
                            modelViewVm.ResetHistoryCommand.Execute(1);
                        }
                        modelViewVm.UpdateHome();
                    }
                }
            }
            catch (Exception ex)
            {
                _snackbarService.ShowAsync("视图已激活事件处理错误", ex.Message + ex.StackTrace, SymbolRegular.ErrorCircle16, ControlAppearance.Danger);
            }
        }
        public void OnDocumentChanged(object sender, DocumentChangedEventArgs e)
        {
            IList<ElementId> added = e.GetAddedElementIds();
            IList<ElementId> deleted = e.GetDeletedElementIds();
            IList<ElementId> modified = e.GetModifiedElementIds();
            UpdateSnoopableObjects(added, deleted, modified);
        }
        public void OnDocumentOpened(object sender, DocumentOpenedEventArgs e)
        {
            if (e.Document.IsFamilyDocument) return;
            UIApplication.Get().Idling += OnApplicationIdling_RefreshData;
        }
        public void OnDocumentClosed(object sender, DocumentClosedEventArgs e)
        {
            if (e.Document.IsFamilyDocument) return;
            UIApplication.Get().Idling += OnApplicationIdling_ClearData;

        }
        public void OnDocumentSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //当前视图为选择视图，直接刷新
            if (CurrentViewModel.Tab == DataTabType.Selection)
            {
                //清空搜索词，因为当搜索词为id时，即使用户切换选择，界面依然显示搜索结果，影响用户查看选择内容
                SearchBoxMediator.SearchText = string.Empty;
                _ = Snoop(DataTabType.Selection);
            }
            else
            {
                //当前视图不是选择视图，但需要重置选择视图历史,以避免切换至选择视图时获取无效历史
                //(因为切换视图时默认获取历史，但选择视图历史存在时效性，选择改变发生时不再有效)
                GetVmByTab(DataTabType.Selection).UpdateHome();
                GetVmByTab(DataTabType.Selection).ResetHistoryCommand.Execute(1);
            }

            //注册一次性空闲事件刷新因选择构件产生的辅助对象数据，选择改变事件中获取不到
            UIApplication.Get().Idling += OnApplicationIdling_RefreshHome;
        }


        private void OnSearchTextChanged(string value)
        {
            IsClearCommandShow = string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value) ? Visibility.Hidden : Visibility.Visible;
            if (CurrentViewModel.SnoopableViewSource.View != null)
            {
                CurrentViewModel.SnoopableViewSource.View.Refresh();
                CurrentViewModel.TrySelectFirst(false);
            }
        }
    }
}
